#include "GlobalHead.h"
#include "MenuManager.h"
#include "Gui_Application.h"
#include "MainWindow.h"
//#include <QApplication>
#include "Command.h"

MenuItem::MenuItem()
{
}

MenuItem::MenuItem(MenuItem* item)
{
	if (item)
		item->appendItem(this);
}

MenuItem::~MenuItem()
{
	clear();
}

void MenuItem::setCommand(const std::string& name)
{
	_name = name;
}

std::string MenuItem::command() const
{
	return _name;
}

bool MenuItem::hasItems() const
{
	return _items.count() > 0;
}

MenuItem* MenuItem::findItem(const std::string& name)
{
	if (_name == name)
	{
		return this;
	}
	else
	{
		for (QList<MenuItem*>::Iterator it = _items.begin(); it != _items.end(); ++it)
		{
			if ((*it)->_name == name)
				return *it;
		}
	}

	return 0;
}

MenuItem* MenuItem::copy() const
{
	MenuItem* root = new MenuItem;
	root->setCommand(command());

	QList<MenuItem*> items = getItems();
	for (QList<MenuItem*>::ConstIterator it = items.begin(); it != items.end(); ++it)
	{
		root->appendItem((*it)->copy());
	}

	return root;
}

uint MenuItem::count() const
{
	return _items.count();
}

void MenuItem::appendItem(MenuItem* item)
{
	_items.push_back(item);
}

bool MenuItem::insertItem(MenuItem* before, MenuItem* item)
{
	int pos = _items.indexOf(before);
	if (pos != -1)
	{
		_items.insert(pos, item);
		return true;
	}
	else
		return false;
}

void MenuItem::removeItem(MenuItem* item)
{
	int pos = _items.indexOf(item);
	if (pos != -1)
		_items.removeAt(pos);
}

void MenuItem::clear()
{
	for (QList<MenuItem*>::Iterator it = _items.begin(); it != _items.end(); ++it)
		delete *it;
	_items.clear();
}

MenuItem& MenuItem::operator << (const std::string& command)
{
	MenuItem* item = new MenuItem(this);
	item->setCommand(command);
	return *this;
}

MenuItem& MenuItem::operator << (MenuItem* item)
{
	appendItem(item);
	return *this;
}

QList<MenuItem*> MenuItem::getItems() const
{
	return _items;
}

// -----------------------------------------------------------

MenuManager* MenuManager::_instance=0;

MenuManager* MenuManager::getInstance()
{
	if ( !_instance )
		_instance = new MenuManager;
	return _instance;
}

void MenuManager::destruct()
{
	delete _instance;
	_instance = 0;
}

MenuManager::MenuManager()
{
}

MenuManager::~MenuManager()
{
}

void MenuManager::setup(MenuItem* menuItems) const
{
	if (!menuItems)
		return; // empty menu bar

	QMenuBar* menuBar = getMainWindow()->menuBar();
	//menuBar->setUpdatesEnabled(false);

	QList<MenuItem*> items = menuItems->getItems();
	QList<QAction*> actions = menuBar->actions();
	for (QList<MenuItem*>::ConstIterator it = items.begin(); it != items.end(); ++it)
	{
		// search for the menu action
		QAction* action = findAction(actions, QString::fromAscii((*it)->command().c_str()));
		if (!action) {
			// There must be not more than one separator in the menu bar, so
			// we can safely remove it if available and append it at the end
			if ((*it)->command() == "Separator") {
				action = menuBar->addSeparator();
				action->setObjectName(QLatin1String("Separator"));
			}
			else {
				// create a new menu
				std::string menuName = (*it)->command();
				QMenu* menu = menuBar->addMenu(
					QApplication::translate("Workbench", menuName.c_str(),
					0, QApplication::UnicodeUTF8));
				action = menu->menuAction();
				menu->setObjectName(QString::fromAscii(menuName.c_str()));
				action->setObjectName(QString::fromAscii(menuName.c_str()));
			}

			// set the menu user data
			action->setData(QString::fromAscii((*it)->command().c_str()));
		}
		else {
			// put the menu at the end
			menuBar->removeAction(action);
			menuBar->addAction(action);
			action->setVisible(true);
			int index = actions.indexOf(action);
			actions.removeAt(index);
		}

		// flll up the menu
		if (!action->isSeparator())
			setup(*it, action->menu());
	}

	// hide all menus which we don't need for the moment
	for (QList<QAction*>::Iterator it = actions.begin(); it != actions.end(); ++it) {
		(*it)->setVisible(false);
	}

	// enable update again
	//menuBar->setUpdatesEnabled(true);
}

void MenuManager::setup(MenuItem* item, QMenu* menu) const
{
	CommandManager& mgr = Gui_Application::instance->commandManager();
	QList<MenuItem*> items = item->getItems();
	QList<QAction*> actions = menu->actions();
	for (QList<MenuItem*>::ConstIterator it = items.begin(); it != items.end(); ++it) {
		// search for the menu item
		QList<QAction*> used_actions = findActions(actions, QString::fromAscii((*it)->command().c_str()));
		if (used_actions.isEmpty()) {
			if ((*it)->command() == "Separator") {
				QAction* action = menu->addSeparator();
				action->setObjectName(QLatin1String("Separator"));
				// set the menu user data
				action->setData(QLatin1String("Separator"));
				used_actions.append(action);
			}
			else {
				if ((*it)->hasItems()) {
					// Creste a submenu
					std::string menuName = (*it)->command();
					QMenu* submenu = menu->addMenu(
						QApplication::translate("Workbench", menuName.c_str(),
						0, QApplication::UnicodeUTF8));
					QAction* action = submenu->menuAction();
					submenu->setObjectName(QString::fromAscii((*it)->command().c_str()));
					action->setObjectName(QString::fromAscii((*it)->command().c_str()));
					// set the menu user data
					action->setData(QString::fromAscii((*it)->command().c_str()));
					used_actions.append(action);
				}
				else {
					// A command can have more than one QAction
					int count = menu->actions().count();
					// Check if action was added successfully
					if (mgr.addTo((*it)->command().c_str(), menu)) {
						QList<QAction*> acts = menu->actions();
						for (int i=count; i < acts.count(); i++) {
							QAction* a = acts[i];
							// set the menu user data
							a->setData(QString::fromAscii((*it)->command().c_str()));
							used_actions.append(a);
						}
					}
				}
			}
		}
		else {
			for (QList<QAction*>::Iterator it = used_actions.begin(); it != used_actions.end(); ++it) {
				// put the menu item at the end
				menu->removeAction(*it);
				menu->addAction(*it);
				int index = actions.indexOf(*it);
				actions.removeAt(index);
			}
		}

		// fill up the submenu
		if ((*it)->hasItems())
			setup(*it, used_actions.front()->menu());
	}

	// remove all menu items which we don't need for the moment
	for (QList<QAction*>::Iterator it = actions.begin(); it != actions.end(); ++it) {
		menu->removeAction(*it);
	}
}

void MenuManager::retranslate() const
{
	QMenuBar* menuBar = getMainWindow()->menuBar();
	QList<QAction*> actions = menuBar->actions();
	for (QList<QAction*>::Iterator it = actions.begin(); it != actions.end(); ++it) {
		if ((*it)->menu())
			retranslate((*it)->menu());
	}
}

void MenuManager::retranslate(QMenu* menu) const
{
	// Note: Here we search for all menus and submenus to retranslate their
	// titles. To ease the translation for each menu the native name is set
	// as user data. However, there are special menus that are created by
	// actions for which the name of the according command name is set. For
	// such menus we have to use the command's menu text instaed. Examples
	// for such actions are Std_RecentFiles, Std_Workbench or Std_FreezeViews.
	CommandManager& mgr = Gui_Application::instance->commandManager();
	QByteArray menuName = menu->menuAction()->data().toByteArray();
	Command* cmd = mgr.getCommandByName(menuName);
	if (cmd) {
		menu->setTitle(
			QApplication::translate(cmd->className(),
			cmd->getMenuText(),
			0, QCoreApplication::CodecForTr));
	}
	else {
		menu->setTitle(
			QApplication::translate("Workbench",
			(const char*)menuName,
			0, QApplication::UnicodeUTF8));
	}
	QList<QAction*> actions = menu->actions();
	for (QList<QAction*>::Iterator it = actions.begin(); it != actions.end(); ++it) {
		if ((*it)->menu()) {
			retranslate((*it)->menu());
		}
	}
}

QAction* MenuManager::findAction(const QList<QAction*>& acts, const QString& item) const
{
	for (QList<QAction*>::ConstIterator it = acts.begin(); it != acts.end(); ++it) {
		if ((*it)->data().toString() == item)
			return *it;
	}

	return 0; // no item with the user data found
}

QList<QAction*> MenuManager::findActions(const QList<QAction*>& acts, const QString& item) const
{
	// It is possible that the user text of several actions match with 'item'.
	// But for the first match all following actions must match. For example
	// the Std_WindowsMenu command provides several actions with the same user
	// name.
	bool first_match = false;
	QList<QAction*> used;
	for (QList<QAction*>::ConstIterator it = acts.begin(); it != acts.end(); ++it) {
		if ((*it)->data().toString() == item) {
			used.append(*it);
			first_match = true;
			// get only one separator per request
			if (item == QLatin1String("Separator"))
				break;
		}
		else if (first_match)
			break;
	}

	return used;
}

void MenuManager::setupContextMenu(MenuItem* item, QMenu &menu) const
{
	setup(item, &menu);
}
